#include <sys/socket.h>
#include <netinet/in.h>
#include <memory.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <map>
#include <list>
#include <linux/tcp.h>


using namespace std;

// ip+端口信息
typedef struct
{
	const char *ip;
	int port;
} address;

typedef struct
{
	int nClientSocket;
	sockaddr_in ServerAddress;
} socket_data;

// 商品信息
typedef struct clientObj
{
    int m_id; // 商品id
    int price; // 商品价格
    std::string name; // 商品名称
    int connectSocket; // socket信息
}clientObj, *pclientObj;


template <typename ConnectionPorcessor>
class CTCPClient : public ConnectionPorcessor
{
public:
	CTCPClient(vector<address> addresses)
	{
		// 这应该是个数组端口和ip
		nServerAddress = addresses;
		printf("nServerAddress的大小%ld\n", nServerAddress.size());

		// m_nServerPort = nServerPort;

		// int nlength = strlen(strServerIP);
		// m_strServerIP = new char[nlength + 1];
		// strcpy(m_strServerIP, strServerIP);
	}

	virtual ~CTCPClient()
	{
		// delete[] nServerAddress;
		delete[] m_strServerIP;
	}

public:
	int Run()
	{
		int address_num = nServerAddress.size();
		printf("address_num的大小是%d\n", address_num);
		socket_data socket_data_list[address_num];
		for (int i = 0; i < address_num; i++)
		{

			socket_data_list[i].nClientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
			if (-1 == socket_data_list[i].nClientSocket)
			{
				std::cout << "socket error" << std::endl;
				return -1;
			}

			memset(&socket_data_list[i].ServerAddress, 0, sizeof(sockaddr_in));
			socket_data_list[i].ServerAddress.sin_family = AF_INET;
			if (::inet_pton(AF_INET, nServerAddress[i].ip, &socket_data_list[i].ServerAddress.sin_addr) != 1)
			{
				std::cout << "inet_pton error" << std::endl;
				::close(socket_data_list[i].nClientSocket);
				return -1;
			}

			socket_data_list[i].ServerAddress.sin_port = htons(nServerAddress[i].port);
		}

		bool breakBool = true;
		vector<int> nClientSocket_list;
		while (breakBool)
		{
			// 同时连接两个服务器。
			nClientSocket_list.clear();
			// 与多个ip建立连接
			for (int i = 0; i < sizeof(socket_data_list) / sizeof(socket_data_list[0]); i++)
			{
				if (::connect(socket_data_list[i].nClientSocket, (sockaddr *)&socket_data_list[i].ServerAddress, sizeof(socket_data_list[i].ServerAddress)) == -1)
				{
					std::cout << "connect error" << std::endl;
					::close(socket_data_list[i].nClientSocket);
					return -1;
				}
				else
				{
					printf("添加了服务器%d\n", socket_data_list[i].nClientSocket);
					nClientSocket_list.push_back(socket_data_list[i].nClientSocket);
				}
			}
			ConnectionPorcessor *pProcess = static_cast<ConnectionPorcessor *>(this);
			// 跳转到父类的ClientFunction中
			pProcess->ClientFunction(&nClientSocket_list, &breakBool);
		}
		for (int i = 0; i < nClientSocket_list.size(); i++)
		{
			::close(nClientSocket_list[i]);
		}

		return 0;
	}

private:
	vector<address> nServerAddress;
	int m_nServerPort;
	char *m_strServerIP;
};

class CMyTCPClient
{
public:
	CMyTCPClient()
	{
	}

	virtual ~CMyTCPClient()
	{
	}

	void ClientFunction(vector<int> *nConnectedSocket, bool *breakbool)
	{
		cout << "欢迎使用客户端，您的id为" << m_userId << endl;
		// 初始化 得到多个服务器的商品id、名称、价格信息、以及socket连接信息
		// 对应服务器端case 0
		initClientData(*nConnectedSocket);
		showUserInfo();
		bool breakbool2 = true;
		while (breakbool2)
		{
			showAllInfo();
			// 展示购物车信息
			showGoods2count();
			// 用户输入
			int opt;
			cout<<"输入想进行的操作：";
			cin >> opt;
			char buf[100];
			// 购买所用的商品id
			int itemId;
			// 购买的数量
			int num;
			string s;
			int respond;
			string strMsg;

			switch (opt)
			{
			case 1:
				{
					cout << "输入购买的商品id(1,2,3,4): " << endl;
					cin >> itemId;
					cout << "输入购买个数(输入数字): " << endl;
					cin >> num;
					cout << "正在检查库存是否足够" << endl;
					strMsg = to_string(m_userId) + " " + to_string(itemId) + " " + to_string(num);
					sendMessageInt(goods2ip[itemId], 1);
					bool isReconnection = sendMessageStr(goods2ip[itemId], strMsg, 1);
					// 反序列化
					while (isReconnection)
					{
						for (vector<int>::iterator iter = nConnectedSocket->begin(); iter != nConnectedSocket->end(); iter++)
						{
							//从vector中删除断连接的元素
							if (*iter == goods2ip[itemId])
							{
								nConnectedSocket->erase(iter);
								break;
							}
						}
						/* code */
						// 开始反序列化
						startDeserialize((*nConnectedSocket)[0]);
						cout<<"反序列化成功"<<endl;
						// 重新请求1业务
						sendMessageInt(goods2ip[itemId], 1);
						cout<<"重新请求1业务" << goods2ip[itemId] << endl;
						isReconnection = sendMessageStr(goods2ip[itemId], strMsg, 2);
						
					}
					requireMessageStr(goods2ip[itemId], buf);
					s = buf;
					respond = stoi(s);
					if (respond == 1)
					{
						goods2count[itemId] += num;
						cout << "购买成功" << endl;
					}
					else
						cout << "库存不足" << endl;
					break;
				}

			// 结算金额
			case 2:
				// 防止变量穿透
				{
					// 结算商品的计算应该前端做
					cout << "#######################" << endl;
					cout << "正在为您结算" << endl;
					int money = countMoney();
					cout << "已花费金额为" << money << endl;
					cout << "#######################" << endl;
					break;
				}
			// 客户端退出
			case 3:
				breakbool2 = false;
				*breakbool = false;
				// 把客户端连接的所有服务器线程关闭
				for (vector<int>::iterator it = nConnectedSocket->begin(); it != nConnectedSocket->end(); it++)
				{
					sendMessageInt(*it, 3);
				}
				cout << "客户端退出" << endl;
				break;
			// 序列化主机
			case 4:
				// cout << "选择想要序列化操作的服务器（A or B）" << endl;
				// sendMessageInt(nConnectedSocket, 4);
				// initClientData(nConnectedSocket);
				break;

			case 5:
				// cout << "选择想要反序列化操作的服务器（A or B）" << endl;

				// sendMessageInt(nConnectedSocket, 5);
				// initClientData(nConnectedSocket);
				break;
			case 6:
				cout << "服务A宕机啦" << endl;
				// sendMessageInt(nConnectedSocket, 6);
				break;
			// 测试服务器A
			case 7:
				cout << "测试6" << endl;
				cout << "欢迎使用客户端，您的id为" << m_userId << endl;
				cout << "nConnectedSocket[0]:" << (*nConnectedSocket)[0] << endl;
				sendMessageInt((*nConnectedSocket)[0], 7);
				::read((*nConnectedSocket)[0], buf, 100);
				cout << "接受到了服务器" << (*nConnectedSocket)[0] << "信息" << buf << endl;
				break;
			// 测试服务器B
			case 8:
				cout << "测试7" << endl;
				cout << "欢迎使用客户端，您的id为" << m_userId << endl;
				cout << "nConnectedSocket[0]:" << (*nConnectedSocket)[0] << endl;
				sendMessageInt((*nConnectedSocket)[1], 7);
				::read((*nConnectedSocket)[1], buf, 100);
				cout << "接受到了服务器" << (*nConnectedSocket)[0] << "信息" << buf << endl;
				break;
			}
		}
	}

	// 通知反序列化
	void startDeserialize(int nConnectedSocket)
	{
		sendMessageInt(nConnectedSocket, 5);
		vector<int> temp = {nConnectedSocket};
		// 购物车不要清空
		initClientData(temp, false);
	}

	// 算钱
	int countMoney()
	{
		int resMoney = 0;
		map<int, int>::iterator iter;
		iter = goods2count.begin();
		while (iter != goods2count.end())
		{
			// 价格*数目
			resMoney += goods2price[iter->first] * iter->second;
			// 购物车清零
			iter->second = 0;
			iter++;
		}
		return resMoney;
	}

	void initClientData(vector<int> nConnectedSocket, bool cleanGoodsCard = true)
	{
		m_objList.clear();
		char buf[100];
		for (vector<int>::iterator it = nConnectedSocket.begin(); it != nConnectedSocket.end(); it++)
		{
			// 向服务器发起case 0请求
			sendMessageInt((*it), 0);
			requireMessageStr((*it), buf);
			string strBuf = buf;
			stringstream istr(strBuf);
			vector<string> results;
			string out;
			while (istr >> out)
			{
				results.push_back(out);
			}
			int objSize = results.size() / 3;
			clientObj obj;
			for (int i = 0, j; i < objSize; i++)
			{
				j = 3 * i;
				obj.m_id = stoi(results[j]);
				obj.name = results[j + 1];
				obj.price = stoi(results[j + 2]);
				obj.connectSocket = (*it);
				m_objList.push_back(obj);
				// 建立反向映射
				goods2ip[stoi(results[j])] = (*it);
				if (cleanGoodsCard){
					goods2count[stoi(results[j])] = 0;
				}
				goods2price[stoi(results[j])] = stoi(results[j + 2]);
			}
		}
	}

	void showUserInfo()
	{
		cout <<"用户id："<< m_userId << "进入了客户端" << endl;
	}

	void showAllInfo()
	{
		cout << "*******************************************" << endl;
		cout << "            商品列表" << endl;
		cout << "\t商品id\t"
			 << "商品名称\t"
			 << "商品价格\t" << endl;
		for (vector<clientObj>::iterator good = m_objList.begin(); good != m_objList.end(); good++)
		{
			cout <<"\t" <<good->m_id << '\t' << good->name << "       \t" << good->price << '\t' << endl;
		}
		cout << "**********************************************************" << endl;
		cout << "请选择您想要的操作: " << endl;
		cout << "  \t输入1-->选择想要购买的商品" << endl;
		cout << "  \t输入2-->购物车结算" << endl;
		cout << "  \t输入3-->退出客户端" << endl;
		cout << "  \t||  宕机操作   可以随意ctrl c掉一个服务器    然后  ||" << endl;
		cout << "  \t||  输入1购买对应宕机服务器的商品就会触发业务搬迁  ||" << endl;
		cout << "**********************************************************" << endl;
	}
	// 展示购物车信息
	void showGoods2count()
	{
		cout <<"购物车信息" <<endl;
		cout << "  \t商品id\t"
			 << "  \t商品数目\t" << endl;
		map<int, int>::iterator iter;
		iter = goods2count.begin();
		while (iter != goods2count.end())
		{
			cout <<"  \t"<< iter->first << "\t \t" << iter->second << endl;
			iter++;
		}
		cout << "*******************************************"  << endl;
	}

	// 展示商品所连接的socket
	void showGoodsToIp()
	{
		
		cout << "   \t商品id\t"
			 << "   \t所在服务器（socket）\t" << endl;
		map<int, int>::iterator iter;
		iter = goods2ip.begin();
		while (iter != goods2ip.end())
		{
			cout << iter->first << " : " << iter->second << endl;
			iter++;
		}
		cout << "***********************************" << endl;
	}

	void sendMessageInt(int nConnectedSocket, int msg)
	{
		char buf[100];
		sprintf(buf, "%d %d", m_userId, msg);
		::write(nConnectedSocket, buf, 100);
	}

	bool sendMessageStr(int nConnectedSocket, string str, int flag = 0)
	{
		if (flag == 1)
		{
			char test[100];
			int a = ::read(nConnectedSocket, test, 100);
			// 服务器断连
			if (a == 0)
			{
				cout << "原来的服务器宕机了，正在进行业务搬迁" << endl;
				return true;
			}
		}else if(flag==2){
			// 不知道为啥呀，为啥会多一次read
			char test[100];
			::read(nConnectedSocket, test, 100);
			::read(nConnectedSocket, test, 100);
		}
		char buf[100];
		strcpy(buf, str.c_str());
		::write(nConnectedSocket, buf, 100);
		return false;
	}

	void requireMessageStr(int nConnectedSocket, char *tmp)
	{
		::read(nConnectedSocket, tmp, 100);
	}

public:
	int m_userId;
	// 商品信息
	vector<clientObj> m_objList;
	// 商品到服务器的映射
	map<int, int> goods2ip;
	// 商品到价格的映射
	map<int, int> goods2price;
	// 购物车信息
	map<int, int> goods2count;
};

int main(int argc, char **argv)
{
	if (argc == 2)
	{
		address n1;
		n1.ip = "127.0.0.1";
		n1.port = 4000;
		address n2;
		n2.ip = "127.0.0.1";
		n2.port = 5000;

		vector<address> addresses;
		addresses.push_back(n1);
		addresses.push_back(n2);

		CTCPClient<CMyTCPClient> client(addresses);
		client.m_userId = atoi(argv[1]);
		client.Run();

	}
	return 0;
}
